package com.batchengine.system;

//import sun.management.ManagementFactory;

import javax.management.Notification;
import javax.management.NotificationEmitter;
import javax.management.NotificationListener;
import java.lang.management.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * Author  : SunilKalva
 * User     : skalva@ymail.com
 * Date     : July 26, 2009
 * <p/>
 * Copyright (c) 2007 Incubator Incorporated. All Rights Reserved.
 * <p/>
 * This software is the confidential and proprietary information of
 * Incubator, Inc. Use is subject to license terms.
 * <p/>
 * <p/>
 * This memory warning system will call the listener when we
 * exceed the percentage of available memory specified.  There
 * should only be one instance of this object created, since the
 * usage threshold can only be set to one number.
 */
public class MemoryWarningSystem {

    private final Collection<Listener> listeners = new ArrayList<Listener>();
    private static final MemoryPoolMXBean tenuredGenPool = findHeapMemPool();

    public interface Listener {

        public void memoryUsageLow(long usedMemory, long maxMemory);
    }

    public MemoryWarningSystem() {

        MemoryMXBean mbean = ManagementFactory.getMemoryMXBean();
        NotificationEmitter emitter = (NotificationEmitter) mbean;
        emitter.addNotificationListener(new NotificationListener() {

            public void handleNotification(Notification n, Object hb) {
                if(n.getType().equals(MemoryNotificationInfo.MEMORY_THRESHOLD_EXCEEDED)) {
                    long maxMemory = tenuredGenPool.getUsage().getMax();
                    long usedMemory = tenuredGenPool.getUsage().getUsed();
                    for(Listener listener : listeners) {
                        listener.memoryUsageLow(usedMemory, maxMemory);
                    }
                }
            }
        }, null, null);
    }

    public boolean addListener(Listener listener) {
        return listeners.add(listener);
    }

    public boolean removeListener(Listener listener) {
        return listeners.remove(listener);
    }

    public static void setPercentageUsageThreshold(double percentage) {
        if(percentage <= 0.0 || percentage > 1.0) {
            throw new IllegalArgumentException("Percentage not in range");
        }
        long maxMemory = tenuredGenPool.getUsage().getMax();
        long warningThreshold = (long) (maxMemory * percentage);
        tenuredGenPool.setUsageThreshold(warningThreshold);
    }

    private static MemoryPoolMXBean findHeapMemPool() {

        for(MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) {

            // I don't know whether this approach is better, or whether
            // we should rather check for the pool name "Tenured Gen"?
            if(pool.getType() == MemoryType.HEAP && pool.isUsageThresholdSupported()) {
                return pool;
            }
        }

        throw new AssertionError("Could not find tenured space");
    }

    /**
     * Tenured Space Pool can be determined by it being of type
     * HEAP and by it being possible to set the usage threshold.
     */
    public static MemoryPoolMXBean dispJavaMBeans() {

        List<GarbageCollectorMXBean> mxBeans = ManagementFactory.getGarbageCollectorMXBeans();
        System.out.println("--------------- Garbage Collector-----------------");
        for(GarbageCollectorMXBean garbageCollectorMXBean : mxBeans) {
            System.out.println(garbageCollectorMXBean.getName());
            System.out.println(garbageCollectorMXBean.getCollectionCount());
            String[] strings = garbageCollectorMXBean.getMemoryPoolNames();
            for(String string : strings) {
                System.out.println("\t\tpool name " + string);
            }
        }
        System.out.println("--------------- Garbage Collector-----------------");
        System.out.println("");

        MemoryMXBean bean = ManagementFactory.getMemoryMXBean();
        System.out.println("--------------- Memmeory Used GMapBean-----------------");
        System.out.println(bean.getHeapMemoryUsage().getMax());
        System.out.println(bean.getHeapMemoryUsage().getUsed());
        System.out.println("--------------- Memmeory Used GMapBean-----------------");
        System.out.println("");

        RuntimeMXBean operatingSystemMXBean = ManagementFactory.getRuntimeMXBean();
        System.out.println("--------------- RUNTIME GMapBean-----------------");
        System.out.println(operatingSystemMXBean.getName());
        System.out.println(operatingSystemMXBean.getSystemProperties());
        System.out.println(operatingSystemMXBean.getInputArguments());
        System.out.println(operatingSystemMXBean.getUptime());
        System.out.println("--------------- RUNTIME GMapBean-----------------");
        System.out.println("");

        System.out.println("--------------- MEM GMapBean-----------------");
        for(MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) {

            System.out.println(pool.getName());
            // I don't know whether this approach is better, or whether
            // we should rather check for the pool name "Tenured Gen"?
            if(pool.getType() == MemoryType.HEAP && pool.isUsageThresholdSupported()) {
                return pool;
            }
        }
        System.out.println("--------------- MEM GMapBean-----------------");

        throw new AssertionError("Could not find tenured space");
    }
}

